import * as THREE from "../../../modules/three/three.module.js";
import PluginBase from "./PluginBase.js";
import {KeyboardState, MouseButton} from "../lib/CommonConsts.js";
import {getGridOrigin, getOldToNewAngle} from "../lib/util/StaticMethods.js";
import {meshToTriangles} from "../shaping/ShapeExtract.js";
import {arrAsubB, debounce, removeArrayItem} from "../lib/util/ToolMethods.js";
import SimpleMarkEntity from "../lib/gadget/SimpleMarkEntity.js";
import WorkArea from "../model/WorkArea.js";
import WorkAreaMesh from "../lib/gadget/WorkAreaMesh.js";

const a1 = new THREE.Vector3()
const _v0 = new THREE.Vector3()
const v0 = new THREE.Vector3()
const v1 = new THREE.Vector3()
const v2 = new THREE.Vector3()
const vA = new THREE.Vector3()
const vB = new THREE.Vector3()
const _ray = new THREE.Ray()
const _raycaster = new THREE.Raycaster()
const unitZ = new THREE.Vector3(0,0,1)
const unitY = new THREE.Vector3(0,1,0)
const unitX = new THREE.Vector3(1,0,0)
const Axis = {X:'x', Y:'y', Z:'z', NONE:'none', array: ['z', 'y', 'x']}
const eventRotateFrame = {
  type: 'rotate frame',
}
const eventScaleGap = {
  type: 'scale gap',
  value: 0.5,
}
const axesHelper = new THREE.AxesHelper(3)
function createSegment(size, color) {
  const segmentGeo = new THREE.BufferGeometry()
  segmentGeo.setFromPoints([new THREE.Vector3(0, -0.5 * size,0), new THREE.Vector3(0,0.5 * size,0)])
  const mtl = new THREE.MeshBasicMaterial({color: color, depthTest: true, depthWrite: true})
  const seg = new THREE.LineSegments(segmentGeo, mtl)
  seg.renderOrder = 20
  return seg
}
function updateSegment(segment, p0, p1) {
  segment.position.copy(p0).lerp(p1, 0.5)
  segment.scale.y = p0.distanceTo(p1)
  a1.copy(p1).sub(p0).normalize()
  segment.quaternion.setFromUnitVectors(unitY, a1)
  segment.visible = true
}
const midSegLength = 30
const midSeg = createSegment(midSegLength, '#f00')
const midSegPlane = new THREE.Mesh(new THREE.PlaneGeometry(1, midSegLength), new THREE.MeshBasicMaterial({color: '#0b0', wireframe: true, side: THREE.DoubleSide}))
midSeg.add(midSegPlane)
midSegPlane.visible = false
const planeMeasure = new THREE.Plane()
function createArrowMesh(a, b, color) {
  const v0 = new THREE.Vector3(-a, 0, 0)
  const v1 = new THREE.Vector3(a, 0, 0)
  const v2 = new THREE.Vector3(a + b, 0, 0)
  const v3 = new THREE.Vector3(0, a + b, 0)
  const v4 = new THREE.Vector3(-a - b, 0, 0)
  const v5 = new THREE.Vector3(0, a , 0)
  const indices = [1,2,3, 3,5,1, 3,4,0, 3,0,5]
  const geometry = new THREE.BufferGeometry().setFromPoints([v0,v1,v2,v3,v4,v5])
  geometry.setIndex(indices)
  const mtl = new THREE.MeshBasicMaterial({color: color, opacity: 0.5, transparent: true, side: THREE.DoubleSide})
  const mesh = new THREE.Mesh(geometry, mtl)
  return mesh
}

function retrieveCutPlanes(matrix, spanHalf = 0.5) {
  const planeA = new THREE.Plane()
  const planeB = new THREE.Plane()
  const pos = new THREE.Vector3().setFromMatrixPosition(matrix)
  const axisX = new THREE.Vector3().setFromMatrixColumn(matrix, 0)
  const posA = new THREE.Vector3().copy(axisX).multiplyScalar(spanHalf).add(pos)
  const posB = new THREE.Vector3().copy(axisX).multiplyScalar(-spanHalf).add(pos)
  planeA.setFromNormalAndCoplanarPoint(axisX, posA)
  planeB.setFromNormalAndCoplanarPoint(axisX, posB)
  return [planeA, planeB]
}
const line3 = new THREE.Line3()
function getTrianglePlaneIntersection(triangle, plane) {
  const {a, b, c} = triangle
  line3.start.copy(a)
  line3.end.copy(b)
  const arr = []
  let t = getSegmentPlaneIntersection(a, b, plane)
  if (t) {
    arr.push(t)
  }
  t = getSegmentPlaneIntersection(b, c, plane)
  if (t) {
    const e = arr.find(it => it.manhattanDistanceTo(t) <= 0.01)
    if (!e) {
      arr.push(t)
    }
  }
  t = getSegmentPlaneIntersection(c, a, plane)
  if (t) {
    const e = arr.find(it => it.manhattanDistanceTo(t) <= 0.01)
    if (!e) {
      arr.push(t)
    }
  }
  return arr
}
function getSegmentPlaneIntersection(a, b, plane, target = new THREE.Vector3()) {
  line3.start.copy(a)
  line3.end.copy(b)
  return plane.intersectLine(line3, target)
}
function getPlaneSegmentsIntersection(plane, segments, refPoint, target) {
  const segPoints = segments.userData.segPoints
  const arr = []
  const dists = []
  for (let i = 0; i < segPoints.length; i += 2) {
    const inter = getSegmentPlaneIntersection(segPoints[i], segPoints[i+1], plane)
    if (inter) {
      arr.push(inter)
      dists.push(refPoint.distanceToSquared(inter))
    }
  }
  if (!dists.length) {
    return null
  }
  const min = Math.min(...dists)
  const minIndex = dists.indexOf(min)
  target.copy(arr[minIndex])
  return target
}
function getConvexPlaneSegments(triangles, plane) {
  // const triangles = meshToTriangles(convexMesh, [])
  const segPoints = []
  for (let i = 0; i < triangles.length; i++) {
    const arr = getTrianglePlaneIntersection(triangles[i], plane)
    if (arr.length === 2) {
      segPoints.push(...arr)
    }
  }
  const lineMat = new THREE.LineBasicMaterial({
    color: '#0e441f',
    transparent: true,
    depthTest: true,
    depthWrite: true,
  })
  const lineGeo = new THREE.BufferGeometry()
  lineGeo.setFromPoints(segPoints)
  const lineSegment = new THREE.LineSegments(lineGeo, lineMat)
  lineSegment.userData.segPoints = segPoints
  return lineSegment
}
function createCuttingSegments() {
  const {_convexTriangles, _cutPlanes, cuttingSegments, world3d} = this
  cuttingSegments.forEach(it => it.removeFromParent())
  cuttingSegments.length = 0
  _cutPlanes.forEach(plane => {
    const seg = getConvexPlaneSegments(_convexTriangles, plane)
    world3d.scene.add(seg)
    cuttingSegments.push(seg)
  })
}
function showText(txtMark, distance, position) {
  txtMark.visible = true
  txtMark.position.copy(position)
  txtMark.text = distance.toFixed(3) + 'm';
  txtMark.updateTextAndSize();
}
class ScaleGapTool extends THREE.Group {
  halfSpan = 0.5
  _pressing = false
  seg
  planeA
  planeB
  _matInv = new THREE.Matrix4()
  downPos = new THREE.Vector3()
  movePos = new THREE.Vector3()
  downLen = 0
  constructor(half) {
    super();
    this.halfSpan = half
    const segmentGeo = new THREE.BufferGeometry()
    segmentGeo.setFromPoints([new THREE.Vector3( -0.5,0,0), new THREE.Vector3(0.5,0,0)])
    const seg = new THREE.LineSegments(segmentGeo, new THREE.MeshBasicMaterial({color: '#ff6000'}))
    seg.scale.set(30, 1, 1)
    const planeMtl = new THREE.MeshBasicMaterial({color: '#bb0', transparent: true, opacity: 0.4, side: THREE.DoubleSide})
    const planeGeo = new THREE.PlaneGeometry(0.7, 30)
    const planeA = new THREE.Mesh(planeGeo, planeMtl)
    const planeB = new THREE.Mesh(planeGeo, planeMtl)
    const planeHit = new THREE.Mesh(new THREE.PlaneGeometry(30, 1), planeMtl)
    planeHit.visible = false
    planeB.rotation.y = planeA.rotation.y = Math.PI * 0.5
    planeA.position.x = -this.halfSpan
    planeB.position.x = this.halfSpan
    const anchorA = getGridOrigin(15, '#bb0')
    const anchorB = getGridOrigin(15, '#bb0')
    anchorA.position.copy(planeA.position)
    anchorB.position.copy(planeB.position)
    this.seg = seg
    this.planeA = planeA
    this.planeB = planeB
    this.planeHit = planeHit
    this.anchorA = anchorA
    this.anchorB = anchorB
    this.add(seg, planeA, planeB, anchorA, anchorB, planeHit)
    this.add(new THREE.AxesHelper(1))
  }
  applyPosDir(pos, dir) {
    this.position.copy(pos)
    this.quaternion.setFromUnitVectors(unitZ, dir)
  }
  setFromRotateTool(rotateTool) {
    this.position.copy(rotateTool.position)
    this.quaternion.copy(rotateTool.quaternion)
  }
  onPointerDown(event, world3d) {
    this._pressing = event.button === MouseButton.left
    this.updateMatrixWorld()
    this._matInv.copy(this.matrixWorld).invert()
    const intersects = world3d.mouseRaycaster.intersectObject(this.planeHit, false)
    if (!intersects.length) {
      return
    }
    this.downPos.copy(intersects[0].point).applyMatrix4(this._matInv)
    // this.downPos.copy(intersects[0].point).sub(this.position).transformDirection(this._matInv)
    this.downLen = this.downPos.x
    console.log('downPos: ', this.downPos.toArray())
    world3d.cameraControls.enabled = false
  }
  onPointerMove(event, world3d) {
    if (!this._pressing) {
      return
    }
    const intersects = world3d.mouseRaycaster.intersectObject(this.planeHit, false)
    if (!intersects.length) {
      return
    }
    this.movePos.copy(intersects[0].point).applyMatrix4(this._matInv)
    const len = this.movePos.x
    let diff = len - this.downLen
    if (this.downLen < 0) {
      diff = this.downLen - len
    }
    const {planeA, planeB, anchorA, anchorB} = this
    planeA.position.x = -this.halfSpan - diff
    planeB.position.x = this.halfSpan + diff
    anchorA.position.copy(planeA.position)
    anchorB.position.copy(planeB.position)
    eventScaleGap.value = Math.abs(this.halfSpan + diff)
    this.dispatchEvent(eventScaleGap)
  }
  onPointerUp(world3d) {
    this._pressing = false
    this.halfSpan = Math.abs(this.planeB.position.x)
    world3d.cameraControls.enabled = true
  }
}
class RotateTool extends THREE.Group {
  _above = 0.1
  _pressing = false
  _matInv = new THREE.Matrix4()
  rotateAxis = Axis.NONE
  _quatBak = new THREE.Quaternion()
  _startDir = new THREE.Vector3()
  _endDir = new THREE.Vector3()
  _startV2 = new THREE.Vector2()
  _endV2 = new THREE.Vector2()
  hittingPos = new THREE.Vector3()
  yPointTo = new THREE.Vector3()
  zNegPointTo = new THREE.Vector3()
  hittingObj
  meshPlanes = []

  constructor() {
    super();
    const anchor = new THREE.AxesHelper(0.5)
    this.add(anchor)
    this.anchor = anchor
    const arrowTheta = createArrowMesh(0.25, 0.1, '#06d')
    const arrowPhi = createArrowMesh(0.25, 0.1, '#092')
    this.add(arrowTheta)
    this.add(arrowPhi)
    this.arrowTheta = arrowTheta
    this.arrowPhi = arrowPhi
    const mat0 = new THREE.Matrix4().setPosition(0, 0.1 + 0.25, 0)
    arrowTheta.geometry.applyMatrix4(mat0)
    const quat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI * 0.5)
    mat0.makeRotationFromQuaternion(quat).setPosition(0, 0, -0.1 - 0.25)
    arrowPhi.geometry.applyMatrix4(mat0)

    const arrowX = createArrowMesh(0.25, 0.1, '#b00')
    const quat2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI * 0.5)
    quat.premultiply(quat2)
    mat0.makeRotationFromQuaternion(quat)
    arrowX.geometry.applyMatrix4(mat0)
    this.arrowX = arrowX
    this.add(arrowX)

    const mtl = new THREE.MeshBasicMaterial({color: '#062', wireframe: true, side: THREE.DoubleSide})
    const meshXY = new THREE.Mesh(new THREE.PlaneGeometry(55, 55), mtl)
    const meshYZ = new THREE.Mesh(new THREE.PlaneGeometry(55, 55), mtl)
    meshYZ.rotation.y = Math.PI * 0.5
    const meshZX = new THREE.Mesh(new THREE.PlaneGeometry(55, 55), mtl)
    meshZX.rotation.x = -Math.PI * 0.5
    const arr = [meshXY, meshZX, meshYZ]
    arr.forEach(it => it.visible = false)
    this.add(...arr)
    this.meshPlanes = arr
    // this.rotateX(Math.PI)
  }
  applyPosDir(pos, dir0) {
    const dir = dir0.clone().negate()
    this.position.copy(dir).multiplyScalar(-this._above).add(pos)
    this.quaternion.setFromUnitVectors(unitZ, dir)
    this.updateMatrix()
    this.yPointTo.setFromMatrixColumn(this.matrix, 1)
    this.zNegPointTo.setFromMatrixColumn(this.matrix, 2).negate()
  }
  getMatInv() {
    this.updateMatrix()
    return this._matInv.copy(this.matrix).invert()
  }
  onPointerDown(event) {
    this._pressing = event.button === MouseButton.left
    this._quatBak.copy(this.quaternion)
    const arr = [this.arrowTheta, this.arrowPhi, this.arrowX]
    if (arr.includes(this.hittingObj)) {
      this._matInv.copy(this.matrix).invert()
      this._startDir.copy(this.hittingPos).sub(this.position).transformDirection(this._matInv)
    }
  }
  onPointerMove(event, world3d) {
    const {arrowTheta, arrowPhi, arrowX} = this
    const arr = [arrowTheta, arrowPhi, arrowX]
    if (this._pressing) {
      if (!arr.includes(this.hittingObj)) {
        return
      }
      const index = arr.indexOf(this.hittingObj)
      const intersects = world3d.mouseRaycaster.intersectObject(this.meshPlanes[index], false)
      if (!intersects.length) {
        return
      }
      this.quaternion.copy(this._quatBak)
      this._endDir.copy(intersects[0].point).sub(this.position).transformDirection(this._matInv)
      let diff = 0
      switch (this.rotateAxis) {
        case Axis.Z:
          this._startV2.copy(this._startDir)
          this._endV2.copy(this._endDir)
          diff = getOldToNewAngle(this._startV2, this._endV2);
          this.rotateZ(diff)
          break
        case Axis.Y:
          this._startV2.set(this._startDir.x, this._startDir.z)
          this._endV2.set(this._endDir.x, this._endDir.z)
          diff = getOldToNewAngle(this._startV2, this._endV2)
          this.rotateY(-diff) // 注意：这里取负数
          break
        case Axis.X:
          this._startV2.set(this._startDir.y, this._startDir.z)
          this._endV2.set(this._endDir.y, this._endDir.z)
          diff = getOldToNewAngle(this._startV2, this._endV2)
          this.rotateX(diff)
          break
      }
      this.yPointTo.setFromMatrixColumn(this.matrix, 1)
      this.zNegPointTo.setFromMatrixColumn(this.matrix, 2).negate()
      this.dispatchEvent(eventRotateFrame)
    } else {
      this.rotateAxis = Axis.NONE
      arr.forEach(it => it.material.opacity = 0.5)
      world3d.cameraControls.enabled = true
      const intersects = world3d.mouseRaycaster.intersectObjects(arr, false)
      if (!intersects.length) {
        this.hittingObj = null
        return
      }
      const hittingObj = intersects[0].object
      hittingObj.material.opacity = 1
      const index = arr.indexOf(hittingObj)
      this.rotateAxis = Axis.array[index]
      this.hittingObj = hittingObj
      this.hittingPos.copy(intersects[0].point)
      world3d.cameraControls.enabled = false
    }
  }
  onPointerUp() {
    this._pressing = false
  }
}
class BoundItem {
  /**
   * @type {THREE.LineSegments[]}
   */
  segments = []
  points = []
  localY = 0
  constructor(segments, points, localY) {
    this.segments = segments
    this.points = points
    this.localY = localY
  }
  addToScene(scene) {
    this.segments.forEach(it => scene.add(it))
  }
  removeFromScene() {
    this.segments.forEach(it => it.removeFromParent())
  }
}

export default class SurfacePartitionPlugin extends PluginBase {
  /**
   * @type {World3d}
   */
  world3d
  /**
   * @type {SimpleMarkEntity[]}
   */
  textMarks = []
  /**
   * @type {THREE.LineSegments[]}
   */
  segments4Measure = []
  /**
   * @type {BoundItem[]}
   */
  boundItems = []
  /**
   * @type {THREE.Vector3[]}
   */
  boundPoints = []
  _marking = false
  /**
   * @type {RotateTool}
   */
  rotateTool
  /**
   * @type {ScaleGapTool}
   */
  scaleTool
  _convexTriangles = []
  _cutPlanes = []
  cuttingSegments = []
  spanHalf = 0.5

  constructor(world3d) {
    super()
    this.world3d = world3d
    this.rotateTool = new RotateTool()
    world3d.scene.add(this.rotateTool)
    this.rotateTool.visible = false
    const onFrameRotating = this._onFrameRotating.bind(this)
    this.rotateTool.addEventListener(eventRotateFrame.type, onFrameRotating)
    const renewCuttingSegments = createCuttingSegments.bind(this)
    this.debounceRenewCuttingSegments = debounce(renewCuttingSegments, 1000)

    this.scaleTool = new ScaleGapTool(this.spanHalf)
    world3d.scene.add(this.scaleTool)
    this.scaleTool.visible = false
    const that = this
    this.scaleTool.addEventListener(eventScaleGap.type, () => {
      that.spanHalf = eventScaleGap.value
      midSegPlane.scale.x = that.spanHalf * 2
      onFrameRotating()
    })
    const txtOption = SimpleMarkEntity.getTxtOption();
    txtOption.fontSize = 0.35;
    for (let i = 0; i < 3; i++) {
      const txtMark = new SimpleMarkEntity('xx', txtOption)
      txtMark.renderOrder = 14;
      this.textMarks.push(txtMark)
      txtMark.setEye(world3d.camera)
      txtMark.visible = false
      world3d.scene.add(txtMark)
      const segment = createSegment(1, '#06d')
      segment.visible = false
      world3d.scene.add(segment)
      this.segments4Measure.push(segment)
    }
  }
  _onFrameRotating() {
    this.rotateTool.updateMatrix()
    this.rotateTool.add(midSeg)
    midSeg.position.z = this.rotateTool._above
    this._cutPlanes = retrieveCutPlanes(this.rotateTool.matrix, this.spanHalf)
    this.debounceRenewCuttingSegments()
  }
  toggleScaleControlVisible(visible) {
    if (!this.rotateTool.visible && !this.scaleTool.visible) {
      return
    }
    this.rotateTool.visible = !visible
    this.scaleTool.visible = visible
    if (visible) {
      this.scaleTool.setFromRotateTool(this.rotateTool)
    }
  }
  toggleMarking() {
    this._marking = !this._marking
    if (this._marking) {
      this.toggleScaleControlVisible(false)
    } else {
      this.hideMarks()
    }
  }
  hideMarks() {
    this.textMarks.forEach(it => it.visible = false)
    this.segments4Measure.forEach(it => it.visible = false)
  }
  pointerDown(event) {
    if (this.scaleTool.visible) {
      this.scaleTool.onPointerDown(event, this.world3d)
    }
    if (this.rotateTool.visible) {
      this.rotateTool.onPointerDown(event)
    }
  }
  pointerMove(event) {
    if (this.rotateTool.visible) {
      if (this._marking) {
        const raycaster = this.world3d.mouseRaycaster
        if (this.cuttingSegments.length < 2) {
          return
        }
        const intersects = raycaster.intersectObject(midSegPlane, false)
        if (!intersects.length) {
          return
        }
        planeMeasure.setFromNormalAndCoplanarPoint(this.rotateTool.yPointTo, intersects[0].point)
        v0.copy(this.rotateTool.yPointTo).negate().multiplyScalar(midSegLength).add(this.rotateTool.position)
        _v0.copy(this.rotateTool.zNegPointTo).multiplyScalar(-this.rotateTool._above)
        v0.add(_v0)
        _ray.set(v0, this.rotateTool.yPointTo)
        _ray.intersectPlane(planeMeasure, v1)
        _raycaster.set(v1, this.rotateTool.zNegPointTo)
        _raycaster.ray.direction.negate()
        const convexMesh = this.world3d._surfaceGridPlugin.convexMesh
        const convexInter = _raycaster.intersectObject(convexMesh, false)
        this.hideMarks()
        const [txtA, txtB, txt2] = this.textMarks
        const [segA, segB, seg2] = this.segments4Measure
        if (convexInter[0]) {
          v2.copy(convexInter[0].point)
          const d2 = v2.distanceTo(v1)
          showText(txt2, d2, v1)
        }
        if (getPlaneSegmentsIntersection(planeMeasure, this.cuttingSegments[0], v1, vA)) {
          const dA = vA.distanceTo(v1)
          showText(txtA, dA, vA.clone().lerp(v1, 0.5))
          updateSegment(segA, v1, vA)
          this.boundPoints[0] = vA
        }
        if (getPlaneSegmentsIntersection(planeMeasure, this.cuttingSegments[1], v1, vB)) {
          const dB = vB.distanceTo(v1)
          showText(txtB, dB, vB.clone().lerp(v1, 0.5))
          updateSegment(segB, v1, vB)
          this.boundPoints[1] = vB
        }
        updateSegment(seg2, vA, vB)
        this.boundPoints[2] = v1
      } else {
        this.rotateTool.onPointerMove(event, this.world3d)
      }
    }
    if (this.scaleTool.visible) {
      this.scaleTool.onPointerMove(event, this.world3d)
    }
  }
  pointerUpMoved(event) {
    this.rotateTool.onPointerUp()
    this.scaleTool.onPointerUp(this.world3d)
  }
  pointerUpUnmoved(event) {
    this.rotateTool.onPointerUp()
    const world3d = this.world3d
    const store = this.world3d.store
    const raycaster = world3d.mouseRaycaster
    if (event.button === MouseButton.right) {
      if (store.stateAddingSurfaceArea) {
        store.freeState()
      }
      if (this.rotateTool.visible) {
        this.rotateTool.visible = this.scaleTool.visible = false
        this._marking = false
        midSeg.removeFromParent()
        this.hideMarks()
        if (this.boundItems.length > 1) {
          this.rotateTool.updateMatrix()
          const {matrix, _above} = this.rotateTool
          const subArea = new WorkArea().initBySubSurface(matrix, _above, this.boundItems[0].points, this.boundItems[1].points)
          const subAreaMesh = new WorkAreaMesh().setFromVO(subArea)
          world3d.scene.add(subAreaMesh)
          store.areaMeshes.push(subAreaMesh)
        }
        this.boundItems.forEach(it => it.removeFromScene())
        this.boundItems.length = 0
      }
      return
    }
    if (this._marking) {
      const segments = this.segments4Measure.map(it => it.clone())
      const points = this.boundPoints.map(it => it.clone())
      const local = points[2].applyMatrix4(this.rotateTool.getMatInv())
      const bItem = new BoundItem(segments, points, local.y)
      this.boundItems.push(bItem)
      bItem.addToScene(this.world3d.scene)
      if (this.boundItems.length > 2) {
        this.boundItems.shift().removeFromScene()
      }
    }
    if (store.stateFree) {
      if (this.world3d._selectHolePlugin.holeMesh) {
        return
      }
      const intersects = raycaster.intersectObjects(store.areaMeshes, false)
      if (intersects.length) {
        const selectSubSurface = intersects[0].object
        axesHelper.position.copy(selectSubSurface.position);
        axesHelper.rotation.setFromRotationMatrix(selectSubSurface.matrix);
        world3d.scene.add(axesHelper)
      }
    }
    if (!store.stateAddingSurfaceArea) {
      return
    }
    const convexMesh = world3d._surfaceGridPlugin.convexMesh
    if (!convexMesh) {
      return
    }
    if (!this._convexTriangles.length) {
      meshToTriangles(convexMesh, this._convexTriangles)
    }
    const intersects = raycaster.intersectObject(convexMesh, false)
    if (!intersects.length) {
      return
    }
    this.rotateTool.visible = true
    this.rotateTool.applyPosDir(intersects[0].point, intersects[0].face.normal)
    this._onFrameRotating()
    store.freeState()
  }
}
